﻿using System;
using System.Collections;
using System.Collections.Specialized;
using TechnicalIndicators.DateBased;
using TechnicalIndicators.Points;
using Utils;
using Utils.Wpf;
using Visiblox.Charts;

namespace StockAnalyst2.Plugins.Visualization.Historical
{
	internal class ResultChartVM : ViewModelBase, IDisposable
	{
		#region Attributes

		private readonly ExecutionResultStatistics _statistics;
		private readonly ExponentialAverage _exponentialAverage;

		#endregion

		#region Properties

		public DataSeries<DateTime, decimal> AccumulatedOperationPartials { get; private set; }
		public DataSeries<DateTime, decimal> DailyPartials { get; private set; }
		public DataSeries<DateTime, decimal> EMAResult { get; private set; }
				 
		#endregion

		#region Public Methods

		public ResultChartVM(ExecutionResultStatistics statistics)
		{
			_statistics = statistics;
			_statistics.AccumulatedOperationPartials.CollectionChanged += this.AccumulatedOperationPartials_CollectionChanged; // Disposed
			_statistics.DailyPartials.CollectionChanged += this.DailyPartials_CollectionChanged; // Disposed

			this.DailyPartials = new DataSeries<DateTime, decimal>("Daily Partials");
			this.AccumulatedOperationPartials = new DataSeries<DateTime, decimal>("Accumulated Result");
			this.EMAResult = new DataSeries<DateTime, decimal>("EMA Result");

			_exponentialAverage = new ExponentialAverage();
			_exponentialAverage.Serie.CollectionChanged += this.Serie_CollectionChanged; // Local object
			_exponentialAverage.Source = new ReadWriteLockedObservableCollection<DatedSeriePoint>(_statistics.DailyPartials, new TimeSpan(0, 0, 10));
			_exponentialAverage.Configure(10);
			_exponentialAverage.Start();
		}

		#endregion
		
		#region Event Handlers

		private void DailyPartials_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			if (e.Action == NotifyCollectionChangedAction.Add)
			{
				this.AddPoints(e.NewItems, this.DailyPartials);
			}
			else if (e.Action == NotifyCollectionChangedAction.Reset)
			{
				this.RemovePoints(this.DailyPartials);
				_exponentialAverage.Clear(); // Restart the averager
				_exponentialAverage.Start();
			}
		}

		private void AccumulatedOperationPartials_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			if (e.Action == NotifyCollectionChangedAction.Add)
			{
				this.AddPoints(e.NewItems, this.AccumulatedOperationPartials);
			}
			else if (e.Action == NotifyCollectionChangedAction.Reset)
			{
				this.RemovePoints(this.AccumulatedOperationPartials);
			}
		}

		private void Serie_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			if (e.Action == NotifyCollectionChangedAction.Add)
			{
				this.AddPoints(e.NewItems, this.EMAResult);
			}
			else if (e.Action == NotifyCollectionChangedAction.Reset)
			{
				this.RemovePoints(this.EMAResult);
			}
		}

		#endregion

		#region Private Methods

		private void AddPoints(ICollection points, DataSeries<DateTime, decimal> serie)
		{
			if (base.Dispatcher.CheckAccess())
			{
				lock (serie)
				{
					foreach (DatedSeriePoint newItem in points)
					{
						serie.Add(newItem.X, newItem.Y);
					}
				}
			}
			else
			{
				base.Dispatcher.Invoke(new Action<ICollection, DataSeries<DateTime, decimal>>(this.AddPoints), new object[] { points, serie });
			}
		}

		private void RemovePoints(DataSeries<DateTime, decimal> serie)
		{
			if (base.Dispatcher.CheckAccess())
			{
				lock (serie)
				{
					serie.Clear();
				}
			}
			else
			{
				base.Dispatcher.Invoke(new Action<DataSeries<DateTime, decimal>>(this.RemovePoints), new object[] {serie});
			}
		}

		#endregion

		#region IDisposable

		protected bool Disposed { get; private set; }

		private void Dispose(bool disposing)
		{
			if (!this.Disposed)
			{
				this.InternalDispose(disposing);
			}

			this.Disposed = true;
		}

		protected virtual void InternalDispose(bool disposing)
		{
			if (disposing)
			{
				_statistics.AccumulatedOperationPartials.CollectionChanged -= this.AccumulatedOperationPartials_CollectionChanged;
				_statistics.DailyPartials.CollectionChanged -= this.DailyPartials_CollectionChanged;
			}

			// Dispose here only unmanaged objects 
			// Don’t use managed objects here because maybe 
			// they have been finalized already
		}

		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}

		~ResultChartVM()
		{
			this.Dispose(false);
		}

		#endregion
	}
}
